﻿//-----------------------------------------------------------------------
// <copyright file="WebLogOnSource.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.Engine.Web
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Threading;

    using ChatBox.DomainObjects.ResultData;
    using ChatBox.Interfaces;
    using ChatBox.Utility;

    using DomainObjects.Settings;

    /// <summary>
    /// A source for login cookies
    /// </summary>
    public class WebLogOnSource : ILogOnSource
    {
        #region data

        /// <summary>
        /// repository for settings data
        /// </summary>
        private readonly IServerSettingsRepository settingsRepository;

        /// <summary>
        /// Interface to the error log
        /// </summary>
        private readonly ILogger logger;

        /// <summary>
        /// The thread used
        /// </summary>
        private Thread thread;

        /// <summary>
        /// The login form data
        /// </summary>
        private Dictionary<string, string> loginForm;

        /// <summary>
        /// the server to connect to
        /// </summary>
        private string serverName;

        /// <summary>
        /// the user name used
        /// </summary>
        private string userName;

        /// <summary>
        /// result of sucessfull login
        /// </summary>
        private LogOnResult successResult;

        #endregion

        /// <summary>
        /// Initializes a new instance of the WebLogOnSource class
        /// </summary>
        /// <param name="settingsRepository">the settings source to use</param>
        /// <param name="logger">the error logger interface</param>
        public WebLogOnSource(IServerSettingsRepository settingsRepository, ILogger logger)
        {
            this.settingsRepository = settingsRepository;
            this.logger = logger;
        }

        #region ILogOnSource Members

        /// <summary>
        /// The event on LogOn completed, success or fail
        /// </summary>
        public event EventHandler<EventArgs<LogOnResult>> Completed;

        /// <summary>
        /// Gets a value indicating whether the source has a log on already
        /// </summary>
        public bool HasLogOn
        {
            get
            {
                return (this.successResult != null) && this.successResult.Success;
            }
        }

        /// <summary>
        /// Gets the logon cookie
        /// </summary>
        public Cookie LogOnCookie
        {
            get
            {
                if (this.successResult == null)
                {
                    return null;
                }
                    
                return this.successResult.LogOnCookie;
            }
        }

        /// <summary>
        /// Do the login
        /// </summary>
        public void GetLogOn()
        {
            if (this.HasLogOn)
            {
                // got login details already. Send them out
                this.Completed(this, new EventArgs<LogOnResult>(this.successResult));
            }
            else
            {
                // launch a thread to do it
                ServerSettingsData serverData = this.settingsRepository.ReadSettings();

                this.serverName = serverData.Server;
                this.userName = serverData.UserName;

                this.loginForm = new Dictionary<string, string>
                 {
                    { "op", "login" },
                    { "user", this.userName },
                    { "passwd", serverData.Password }
                 };

                this.thread = new Thread(this.ThreadMain);
                this.thread.IsBackground = true;
                this.thread.Name = "Chatbox login thread";

                this.thread.Start();
            }
        }

        #endregion

        /// <summary>
        /// The thread procedure
        /// </summary>
        private void ThreadMain()
        {
            Uri postUri = new Uri(this.serverName + "/index.pl");

            try
            {
                using (HttpWebResponse response = WebHelpers.MultipartFormDataPost(postUri, EngineConstants.UserAgentString, this.loginForm, null)) 
                {
                    if (response != null)
                    {
                        Cookie logOnCookie = response.Cookies["userpass"];

                        if (logOnCookie == null)
                        {
                            string message = "login failed for " + this.userName + 
                                " cookie not found in " + response.Cookies.Count + " cookies";
                            this.OnLogOnFail(message, null, false);
                        }
                        else
                        {
                            this.OnLogOnSuccess(logOnCookie);                            
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string message = "login failed for " + this.userName + ": " + ex.Message;
                this.OnLogOnFail(message, ex, true);
            }
        }

        /// <summary>
        /// Send a success result 
        /// </summary>
        /// <param name="logOnCookie">the cookie that authenticates login</param>
        private void OnLogOnSuccess(Cookie logOnCookie)
        {
            this.successResult = new LogOnResult(true, logOnCookie);
            this.successResult.Message = "Logged on as " + this.userName;

            if (this.Completed != null)
            {
                this.Completed(this, new EventArgs<LogOnResult>(this.successResult));
            }
        }

        /// <summary>
        /// Send a failure result
        /// </summary>
        /// <param name="message">the message to send</param>
        /// <param name="ex">the exception</param>
        /// <param name="logMessage">Should the details be logged</param>
        private void OnLogOnFail(string message, Exception ex, bool logMessage)
        {
            if (logMessage && (this.logger != null))
            {
                this.logger.LogException(message, ex);
            }

            if (this.Completed != null)
            {
                LogOnResult result = new LogOnResult(false, message, ex);
                this.Completed(this, new EventArgs<LogOnResult>(result));
            }
        }
    }
}